#include "data_structures.hh"
#include <cassert>
#include <iostream>
#include <map>
#include <sstream>
#include <boost/foreach.hpp>

using std::vector;
using std::string;

Adventure::Adventure() :
  turns(0),
  here(0),
  switches(32768),
  counters(vector<int>(32768)) {
  
}

int Adventure::find_object(const string &obj, int adjective) {
  std::map<const Object*, int> res;
  ObjectArray::const_iterator it;

  for(unsigned int i = 0; i < objects.size(); ++i) {
    const Object *curr(&objects[i]);
    if(curr->name == obj) {
      res[curr] = static_cast<int>(i);
    }
  }
  if(res.size() == 1) {
    return res.begin()->second;
  } else if(res.size() > 1) {
    if(adjective >= 0) {
      std::map<const Object*, int> newres;
      for(auto it = res.begin(); it != res.end(); ++it) {
	if(it->first->adjective == adjective) {
	  newres.insert(*it);
	}
      }
      assert(newres.size() <= res.size());
      std::swap(res, newres);
    }
    for(auto it = res.begin(); it != res.end(); ++it) {
      if(it->first->pos == here || it->first->pos == POS_CARRIED) { //Is available
     	return it->second;
      }
    }
    assert(!res.empty());
    //Just return the first found object if no other criteria apply...
    return res.begin()->second;
  }
  return -1;
}

int Adventure::find_object_avail(const string &obj, int adjective) {
  std::map<const Object*, int> res;
  ObjectArray::const_iterator it;

  for(unsigned int i = 0; i < objects.size(); ++i) {
    const Object *curr(&objects[i]);
    if(curr->name == obj && (curr->pos == here || curr->pos == POS_CARRIED)) {
      res[curr] = static_cast<int>(i);
    }
  }
  if(res.size() == 1) {
    return res.begin()->second;
  } else {
    for(auto it = res.begin(); it != res.end(); ++it) {
      if(it->first->adjective == adjective) {
	return it->second;
      }
    }
  }
  return -1;
}

std::string Adventure::get_object_name(int i) const {
  string name;
  const Object &obj = objects.at(i);
  if(obj.adjective >= 0) {
    for(auto it = adjectives.begin(); it != adjectives.end(); ++it) {
      if(it->second == obj.adjective) {
	name = it->first;
	break; //TODO: what if not found?
      }
    }
    name += ' ';
  }
  name += obj.name;
  return name;
}

std::string Adventure::inventory() const {
  vector<int> carried_objs;
  std::ostringstream out;

  for(unsigned int j = 0; j < objects.size(); ++j) {
    if(objects[j].pos == POS_CARRIED) carried_objs.push_back(j);
  }
  if(carried_objs.empty()) {
    out << "You do not carry anything. ";
  } else {
    out << "You carry: ";
    for(unsigned int i = 0; i < carried_objs.size(); ++i) {
      out << get_object_name(carried_objs[i])
	  << ((i < carried_objs.size() - 1) && (objects.size() > 1) ? ", " : ". ");
    }
  }
  return out.str();
}


Object::Object() : pos(-1), weight(0), name("*UNKNOWN*"), desc("*UNKNOWN OBJECT*"), adjective(-1) {}
Object::Object(int p, int w, const std::string& n, const std::string& d, int a) : pos(p), weight(w), name(n), desc(d), adjective(a) {}

